#pragma once

#include <atomic>
#include <mutex>
#include <thread>

namespace kratos { namespace corelib {

// Thread-safety singleton
template <typename ClassType>
class Singleton {
    Singleton() = delete;
    Singleton(const Singleton&) = delete;
    Singleton(Singleton&&) = delete;
    Singleton<ClassType>& operator=(const Singleton&) = delete;

private:
    static std::mutex lock_; // Mutex
    static std::atomic<ClassType*> instance_; // The pointer of Singleton

public:
    // Returns the singleton instance pointer
    // @param args The argument for ctor of ClassType
    // @return singleton instance pointer
    template <typename...Args>
    static ClassType* instance(Args&&...args) {       
        auto current = instance_.load(std::memory_order_acquire);
        if (!current) {
            std::lock_guard<std::mutex> lock(lock_);
            current = instance_.load(std::memory_order_relaxed);
            if (!current) {
                current = new ClassType(std::forward<Args>(args)...);
                instance_.store(current, std::memory_order_release);
            }
        }
        return current;
    }

    // Destroy singleton
    static void destroy() {
        auto current = instance_.load(std::memory_order_acquire);
        if (current) {
            std::lock_guard<std::mutex> lock(lock_);
            current = instance_.load(std::memory_order_relaxed);
            delete current;
            instance_.store(nullptr, std::memory_order_release);
        }
    }
};

template <typename T>
std::mutex Singleton<T>::lock_;

template <typename T>
std::atomic<T*> Singleton<T>::instance_;

}}
